import multiprocessing
import os.path
import time

import allure
import pytest
from playwright.sync_api import Page, sync_playwright

from utils.log import log
from utils.readYamlFile import ReadYamlFile


def get_ignore_case():
    """
    读取执行成功的用例，添加到ignore列表中
    pytest收集用例时，忽略列表中的用例
    """
    data = ReadYamlFile().read_yaml('case_state.yaml')
    ignore_list_passed = [f'script/{case}' for case in data['passed']]
    ignore_list_skipped = [f'script/{case}' for case in data['skipped']]
    ignore_list = ignore_list_passed + ignore_list_skipped
    return ignore_list


# 声明以排除测试目录或模块，需要是路径列表
collect_ignore = get_ignore_case()


class CaseState:
    # 创建进程锁
    __lock = multiprocessing.Lock()

    @classmethod
    def set_case_state(cls, state, test_name):
        """
        更新测试状态
            :param state: 'passed' 'failed' 'skipped'
            :param test_name: 测试类名
        """
        with cls.__lock:
            if state == 'passed':
                cls.__set_case_state_passed(test_name)
            elif state == 'failed':
                cls.__set_case_state_failed(test_name)

    @classmethod
    def __set_case_state_passed(cls, test_name):
        """
        用例状态为成功，则判断类名是否在passed中，若不存在，则添加；并删除其他状态中的类名
            :param test_name: 测试类名
        """
        state_data = ReadYamlFile.read_yaml('case_state.yaml')
        if test_name not in state_data['passed']:
            state_data['passed'].append(test_name)
        if test_name in state_data['failed']:
            state_data['failed'].remove(test_name)
        ReadYamlFile.write_yaml('case_state.yaml', state_data)

    @classmethod
    def __set_case_state_failed(cls, test_name):
        """
        用例状态为失败，则判断类名是否在failed中，若不存在，则添加；并删除其他状态中的类名
            :param test_name: 测试类名
        """
        state_data = ReadYamlFile.read_yaml('case_state.yaml')
        if test_name not in state_data['failed']:
            state_data['failed'].append(test_name)
        if test_name in state_data['passed']:
            state_data['passed'].remove(test_name)
        ReadYamlFile.write_yaml('case_state.yaml', state_data)


class ReportPage:
    """
    ReportPage类，用于储存[失败截图]所需的page实例
    """
    # 初始化page对象
    __page = None

    @classmethod
    def set(cls, page):
        """
        将外部传入的page实例，存储到类变量中
            :param page: page实例
        """
        cls.__page = page

    @classmethod
    def get(cls):
        """
        获取当前处理页面的page实例
        """
        return cls.__page


@pytest.fixture(scope='module')
def create_page() -> Page:
    print('启动playwright服务')
    # 启动playwright服务，创建browser、context实例
    playwright = sync_playwright().start()
    browser = playwright.chromium.launch(headless=False, args=['--start-maximized'])
    context = browser.new_context(ignore_https_errors=True, no_viewport=True)
    context.storage_state()
    # context.tracing.start(screenshots=True, snapshots=True, sources=True)
    page = context.new_page()
    page.set_default_timeout(10000)
    # 将page实例存储到Page类中
    ReportPage.set(page)
    yield ReportPage.get()
    # 结束playwright服务
    page.close()
    # context.tracing.stop(os.path.dirname(__file__) + '\\trace\\trace.zip')
    context.close()
    browser.close()
    playwright.stop()
    print('关闭playwright服务')


@pytest.hookimpl(tryfirst=True, hookwrapper=True)
def pytest_runtest_makereport(item, call):
    # execute all other hooks to obtain the report object
    outcome = yield
    rep = outcome.get_result()
    # 返回用例名称 --> demo_script/test_xdist_demo.py::Test04::test_example_04[java]
    nodeid = item.nodeid
    # 分割字符串，获取用例文件名 test_xdist_demo.py
    test_name = nodeid.split('::')[0].split('/')[1]
    if rep.when == 'call':
        if rep.passed:
            """更新测试状态-passed"""
            CaseState.set_case_state('passed', test_name)
        elif rep.failed:
            try:
                """更新测试状态failed"""
                CaseState.set_case_state('failed', test_name)
                """测试报告输出-页面源码"""
                # timestamp = time.strftime('%Y-%m-%d-%H-%M-%S')
                # root_path = os.path.dirname(os.path.abspath(__file__))
                # page_path = os.sep.join([root_path, f'page_source\\{timestamp}_source.html'])
                # with open(page_path, 'w', encoding='u8') as f:
                #     f.write(ReportPage.get().content())
                # allure.attach(page_path, '页面源码', allure.attachment_type.HTML)
                """测试报告输出-异常截图"""
                # rep.when可选参数有call、setup、teardown
                # call表示为用例执行环节、setup、teardown为环节初始化和清理环节
                # 这里只针对用例执行且失败的用例进行异常截图
                allure.attach(ReportPage.get().screenshot(), '异常截图', allure.attachment_type.JPG)
            except Exception:
                log.warning('无法进行异常截图')


# 添加自定义标签
def pytest_collection_modifyitems(session, config, items: list):
    for item in items:
        if "case" in item.nodeid:
            item.add_marker(pytest.mark.case)
        if "test" in item.nodeid:
            item.add_marker(pytest.mark.test)
